/*-------------------------------------------------------------------------
 * drawElements Quality Program Tester Core
 * ----------------------------------------
 *
 * Copyright 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *//*!
 * \file
 * \brief WGL Utilities.
 *//*--------------------------------------------------------------------*/

#include "tcuWGL.hpp"
#include "tcuWin32Window.hpp"
#include "deDynamicLibrary.hpp"
#include "deMemory.h"
#include "deStringUtil.hpp"
#include "tcuFormatUtil.hpp"
#include "gluRenderConfig.hpp"
#include "glwEnums.hpp"

#include <map>
#include <sstream>
#include <iterator>
#include <set>

#include <WinGDI.h>

// WGL_ARB_pixel_format
#define WGL_NUMBER_PIXEL_FORMATS_ARB				0x2000
#define WGL_DRAW_TO_WINDOW_ARB						0x2001
#define WGL_DRAW_TO_BITMAP_ARB						0x2002
#define WGL_ACCELERATION_ARB						0x2003
#define WGL_NEED_PALETTE_ARB						0x2004
#define WGL_NEED_SYSTEM_PALETTE_ARB					0x2005
#define WGL_SWAP_LAYER_BUFFERS_ARB					0x2006
#define WGL_SWAP_METHOD_ARB							0x2007
#define WGL_NUMBER_OVERLAYS_ARB						0x2008
#define WGL_NUMBER_UNDERLAYS_ARB					0x2009
#define WGL_TRANSPARENT_ARB							0x200A
#define WGL_TRANSPARENT_RED_VALUE_ARB				0x2037
#define WGL_TRANSPARENT_GREEN_VALUE_ARB				0x2038
#define WGL_TRANSPARENT_BLUE_VALUE_ARB				0x2039
#define WGL_TRANSPARENT_ALPHA_VALUE_ARB				0x203A
#define WGL_TRANSPARENT_INDEX_VALUE_ARB				0x203B
#define WGL_SHARE_DEPTH_ARB							0x200C
#define WGL_SHARE_STENCIL_ARB						0x200D
#define WGL_SHARE_ACCUM_ARB							0x200E
#define WGL_SUPPORT_GDI_ARB							0x200F
#define WGL_SUPPORT_OPENGL_ARB						0x2010
#define WGL_DOUBLE_BUFFER_ARB						0x2011
#define WGL_STEREO_ARB								0x2012
#define WGL_PIXEL_TYPE_ARB							0x2013
#define WGL_COLOR_BITS_ARB							0x2014
#define WGL_RED_BITS_ARB							0x2015
#define WGL_RED_SHIFT_ARB							0x2016
#define WGL_GREEN_BITS_ARB							0x2017
#define WGL_GREEN_SHIFT_ARB							0x2018
#define WGL_BLUE_BITS_ARB							0x2019
#define WGL_BLUE_SHIFT_ARB							0x201A
#define WGL_ALPHA_BITS_ARB							0x201B
#define WGL_ALPHA_SHIFT_ARB							0x201C
#define WGL_ACCUM_BITS_ARB							0x201D
#define WGL_ACCUM_RED_BITS_ARB						0x201E
#define WGL_ACCUM_GREEN_BITS_ARB					0x201F
#define WGL_ACCUM_BLUE_BITS_ARB						0x2020
#define WGL_ACCUM_ALPHA_BITS_ARB					0x2021
#define WGL_DEPTH_BITS_ARB							0x2022
#define WGL_STENCIL_BITS_ARB						0x2023
#define WGL_AUX_BUFFERS_ARB							0x2024

#define WGL_NO_ACCELERATION_ARB						0x2025
#define WGL_GENERIC_ACCELERATION_ARB				0x2026
#define WGL_FULL_ACCELERATION_ARB					0x2027

#define WGL_TYPE_RGBA_ARB							0x202B
#define WGL_TYPE_COLORINDEX_ARB						0x202C

// WGL_ARB_color_buffer_float
#define WGL_TYPE_RGBA_FLOAT_ARB						0x21A0

// WGL_EXT_pixel_type_packed_float
#define WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT			0x20A8

// WGL_ARB_multisample
#define WGL_SAMPLE_BUFFERS_ARB						0x2041
#define WGL_SAMPLES_ARB								0x2042

// WGL_EXT_colorspace
#define WGL_COLORSPACE_EXT							0x309D
#define WGL_COLORSPACE_SRGB_EXT						0x3089
#define WGL_COLORSPACE_LINEAR_EXT					0x308A

// WGL_ARB_create_context
#define WGL_CONTEXT_MAJOR_VERSION_ARB				0x2091
#define WGL_CONTEXT_MINOR_VERSION_ARB				0x2092
#define WGL_CONTEXT_LAYER_PLANE_ARB					0x2093
#define WGL_CONTEXT_FLAGS_ARB						0x2094
#define WGL_CONTEXT_PROFILE_MASK_ARB				0x9126
#define WGL_CONTEXT_DEBUG_BIT_ARB					0x0001
#define WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB		0x0002
#define WGL_CONTEXT_CORE_PROFILE_BIT_ARB			0x00000001
#define WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB	0x00000002
#define WGL_CONTEXT_ES_PROFILE_BIT_EXT				0x00000004

// WGL_ARB_create_context_robustness
#define WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB			0x0004
#define WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB	0x8256
#define WGL_NO_RESET_NOTIFICATION_ARB				0x8261
#define WGL_LOSE_CONTEXT_ON_RESET_ARB				0x8252

// WGL ARB_create_context_no_error
#define WGL_CONTEXT_OPENGL_NO_ERROR_ARB				0x31B3

DE_BEGIN_EXTERN_C

// WGL core
typedef HGLRC		(WINAPI* wglCreateContextFunc)				(HDC hdc);
typedef BOOL		(WINAPI* wglDeleteContextFunc)				(HGLRC hglrc);
typedef BOOL		(WINAPI* wglMakeCurrentFunc)				(HDC hdc, HGLRC hglrc);
typedef PROC		(WINAPI* wglGetProcAddressFunc)				(LPCSTR lpszProc);
typedef BOOL		(WINAPI* wglSwapLayerBuffersFunc)			(HDC dhc, UINT fuPlanes);

// WGL_ARB_pixel_format
typedef BOOL		(WINAPI* wglGetPixelFormatAttribivARBFunc)	(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, int *piValues);
typedef BOOL		(WINAPI* wglGetPixelFormatAttribfvARBFunc)	(HDC hdc, int iPixelFormat, int iLayerPlane, UINT nAttributes, const int *piAttributes, FLOAT *pfValues);
typedef BOOL		(WINAPI* wglChoosePixelFormatARBFunc)		(HDC hdc, const int *piAttribIList, const FLOAT *pfAttribFList, UINT nMaxFormats, int *piFormats, UINT *nNumFormats);

// WGL_ARB_create_context
typedef HGLRC		(WINAPI* wglCreateContextAttribsARBFunc)	(HDC hdc, HGLRC hshareContext, const int* attribList);
typedef const char*	(WINAPI* wglGetExtensionsStringARBFunc)		(HDC hdc);

// WGL_EXT_swap_control
typedef BOOL		(WINAPI* wglSwapIntervalEXTFunc)			(int interval);

DE_END_EXTERN_C

namespace tcu
{
namespace wgl
{

// Functions

struct Functions
{
	// Core
	wglCreateContextFunc				createContext;
	wglDeleteContextFunc				deleteContext;
	wglMakeCurrentFunc					makeCurrent;
	wglGetProcAddressFunc				getProcAddress;
	wglSwapLayerBuffersFunc				swapLayerBuffers;

	// WGL_ARB_pixel_format
	wglGetPixelFormatAttribivARBFunc	getPixelFormatAttribivARB;
	wglGetPixelFormatAttribfvARBFunc	getPixelFormatAttribfvARB;
	wglChoosePixelFormatARBFunc			choosePixelFormatARB;

	// WGL_ARB_create_context
	wglCreateContextAttribsARBFunc		createContextAttribsARB;
	wglGetExtensionsStringARBFunc		getExtensionsStringARB;

	// WGL_EXT_swap_control
	wglSwapIntervalEXTFunc				swapIntervalEXT;


	Functions (void)
		: createContext				(DE_NULL)
		, deleteContext				(DE_NULL)
		, makeCurrent				(DE_NULL)
		, getProcAddress			(DE_NULL)
		, swapLayerBuffers			(DE_NULL)
		, getPixelFormatAttribivARB	(DE_NULL)
		, getPixelFormatAttribfvARB	(DE_NULL)
		, choosePixelFormatARB		(DE_NULL)
		, createContextAttribsARB	(DE_NULL)
		, getExtensionsStringARB	(DE_NULL)
	{
	}
};

// Library

class Library
{
public:
								Library			(HINSTANCE instance);
								~Library		(void);

	const Functions&			getFunctions	(void) const	{ return m_functions;	}
	const de::DynamicLibrary&	getGLLibrary	(void) const	{ return m_library;		}
	bool						isWglExtensionSupported (const char* extName) const;

private:
	de::DynamicLibrary			m_library;
	Functions					m_functions;
	std::set<std::string>		m_extensions;
};

Library::Library (HINSTANCE instance)
	: m_library("opengl32.dll")
{
	// Temporary 1x1 window for creating context
	win32::Window tmpWindow(instance, 1, 1);

	// Load WGL core.
	m_functions.createContext		= (wglCreateContextFunc)		m_library.getFunction("wglCreateContext");
	m_functions.deleteContext		= (wglDeleteContextFunc)		m_library.getFunction("wglDeleteContext");
	m_functions.getProcAddress		= (wglGetProcAddressFunc)		m_library.getFunction("wglGetProcAddress");
	m_functions.makeCurrent			= (wglMakeCurrentFunc)			m_library.getFunction("wglMakeCurrent");
	m_functions.swapLayerBuffers	= (wglSwapLayerBuffersFunc)		m_library.getFunction("wglSwapLayerBuffers");

	if (!m_functions.createContext		||
		!m_functions.deleteContext		||
		!m_functions.getProcAddress		||
		!m_functions.makeCurrent		||
		!m_functions.swapLayerBuffers)
		throw ResourceError("Failed to load core WGL functions");

	{
		PIXELFORMATDESCRIPTOR pixelFormatDesc;
		deMemset(&pixelFormatDesc, 0, sizeof(pixelFormatDesc));

		pixelFormatDesc.nSize			= sizeof(pixelFormatDesc);
		pixelFormatDesc.nVersion		= 1;
		pixelFormatDesc.dwFlags			= PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
		pixelFormatDesc.iPixelType		= PFD_TYPE_RGBA;
		pixelFormatDesc.iLayerType		= PFD_MAIN_PLANE;

		int pixelFormat = ChoosePixelFormat(tmpWindow.getDeviceContext(), &pixelFormatDesc);
		if (!SetPixelFormat(tmpWindow.getDeviceContext(), pixelFormat, &pixelFormatDesc))
			throw ResourceError("Failed to set pixel format for temporary context creation");
	}

	// Create temporary context for loading extension functions.
	HGLRC tmpCtx = m_functions.createContext(tmpWindow.getDeviceContext());
	if (!tmpCtx || !m_functions.makeCurrent(tmpWindow.getDeviceContext(), tmpCtx))
	{
		if (tmpCtx)
			m_functions.deleteContext(tmpCtx);
		throw ResourceError("Failed to create temporary WGL context");
	}

	// WGL_ARB_pixel_format
	m_functions.getPixelFormatAttribivARB	= (wglGetPixelFormatAttribivARBFunc)m_functions.getProcAddress("wglGetPixelFormatAttribivARB");
	m_functions.getPixelFormatAttribfvARB	= (wglGetPixelFormatAttribfvARBFunc)m_functions.getProcAddress("wglGetPixelFormatAttribfvARB");
	m_functions.choosePixelFormatARB		= (wglChoosePixelFormatARBFunc)m_functions.getProcAddress("wglChoosePixelFormatARB");

	// WGL_ARB_create_context
	m_functions.createContextAttribsARB		= (wglCreateContextAttribsARBFunc)m_functions.getProcAddress("wglCreateContextAttribsARB");
	m_functions.getExtensionsStringARB		= (wglGetExtensionsStringARBFunc)m_functions.getProcAddress("wglGetExtensionsStringARB");

	// WGL_EXT_swap_control
	m_functions.swapIntervalEXT				= (wglSwapIntervalEXTFunc)m_functions.getProcAddress("wglSwapIntervalEXT");

	m_functions.makeCurrent(tmpWindow.getDeviceContext(), NULL);
	m_functions.deleteContext(tmpCtx);

	if (!m_functions.getPixelFormatAttribivARB	||
		!m_functions.getPixelFormatAttribfvARB	||
		!m_functions.choosePixelFormatARB		||
		!m_functions.createContextAttribsARB	||
		!m_functions.getExtensionsStringARB)
		throw ResourceError("Failed to load WGL extension functions");

	const char* extensions = m_functions.getExtensionsStringARB(tmpWindow.getDeviceContext());
	std::istringstream extStream(extensions);
	m_extensions = std::set<std::string>(std::istream_iterator<std::string>(extStream),
										 std::istream_iterator<std::string>());
}

Library::~Library (void)
{
}

bool Library::isWglExtensionSupported (const char* extName) const
{
	return m_extensions.find(extName) != m_extensions.end();
}

// Core

Core::Core (HINSTANCE instance)
	: m_library(new Library(instance))
{
}

Core::~Core (void)
{
	delete m_library;
}

std::vector<int> Core::getPixelFormats (HDC deviceCtx) const
{
	const Functions& wgl = m_library->getFunctions();

	int attribs[] = { WGL_NUMBER_PIXEL_FORMATS_ARB };
	int values[DE_LENGTH_OF_ARRAY(attribs)];

	if (!wgl.getPixelFormatAttribivARB(deviceCtx, 0, 0, DE_LENGTH_OF_ARRAY(attribs), &attribs[0], &values[0]))
		TCU_THROW(ResourceError, "Failed to query number of WGL pixel formats");

	std::vector<int> pixelFormats(values[0]);
	for (int i = 0; i < values[0]; i++)
		pixelFormats[i] = i+1;

	return pixelFormats;
}

static PixelFormatInfo::Acceleration translateAcceleration (int accel)
{
	switch (accel)
	{
		case WGL_NO_ACCELERATION_ARB:		return PixelFormatInfo::ACCELERATION_NONE;
		case WGL_GENERIC_ACCELERATION_ARB:	return PixelFormatInfo::ACCELERATION_GENERIC;
		case WGL_FULL_ACCELERATION_ARB:		return PixelFormatInfo::ACCELERATION_FULL;
		default:							return PixelFormatInfo::ACCELERATION_UNKNOWN;
	}
}

static PixelFormatInfo::PixelType translatePixelType (int type)
{
	switch (type)
	{
		case WGL_TYPE_RGBA_ARB:					return PixelFormatInfo::PIXELTYPE_RGBA;
		case WGL_TYPE_RGBA_FLOAT_ARB:			return PixelFormatInfo::PIXELTYPE_RGBA_FLOAT;
		case WGL_TYPE_RGBA_UNSIGNED_FLOAT_EXT:	return PixelFormatInfo::PIXELTYPE_RGBA_UNSIGNED_FLOAT;
		case WGL_TYPE_COLORINDEX_ARB:			return PixelFormatInfo::PIXELTYPE_COLOR_INDEX;
		default:								return PixelFormatInfo::PIXELTYPE_UNKNOWN;
	}
}

static void getPixelFormatAttribs (const Functions& wgl, HDC deviceCtx, int pixelFormat, int numAttribs, const int* attribs, std::map<int, int>* dst)
{
	std::vector<int>	values	(numAttribs);

	if (!wgl.getPixelFormatAttribivARB(deviceCtx, pixelFormat, 0, numAttribs, &attribs[0], &values[0]))
		TCU_THROW(ResourceError, "Pixel format query failed");

	for (int ndx = 0; ndx < numAttribs; ++ndx)
		(*dst)[attribs[ndx]] = values[ndx];
}

PixelFormatInfo Core::getPixelFormatInfo (HDC deviceCtx, int pixelFormat) const
{
	static const int	s_attribsToQuery[] =
	{
		WGL_DRAW_TO_WINDOW_ARB,
		WGL_DRAW_TO_BITMAP_ARB,
		WGL_ACCELERATION_ARB,
		WGL_NEED_PALETTE_ARB,
		WGL_NEED_SYSTEM_PALETTE_ARB,
		WGL_NUMBER_OVERLAYS_ARB,
		WGL_NUMBER_UNDERLAYS_ARB,
		WGL_SUPPORT_OPENGL_ARB,
		WGL_DOUBLE_BUFFER_ARB,
		WGL_STEREO_ARB,
		WGL_PIXEL_TYPE_ARB,
		WGL_RED_BITS_ARB,
		WGL_GREEN_BITS_ARB,
		WGL_BLUE_BITS_ARB,
		WGL_ALPHA_BITS_ARB,
		WGL_ACCUM_BITS_ARB,
		WGL_DEPTH_BITS_ARB,
		WGL_STENCIL_BITS_ARB,
		WGL_AUX_BUFFERS_ARB,
		WGL_SAMPLE_BUFFERS_ARB,
		WGL_SAMPLES_ARB,
		WGL_COLORSPACE_EXT
	};
	const Functions&	wgl			= m_library->getFunctions();
	std::map<int, int>	values;

	getPixelFormatAttribs(wgl, deviceCtx, pixelFormat, DE_LENGTH_OF_ARRAY(s_attribsToQuery), &s_attribsToQuery[0], &values);

	// Translate values.
	PixelFormatInfo info;

	info.pixelFormat		= pixelFormat;
	info.surfaceTypes		|= (values[WGL_DRAW_TO_WINDOW_ARB] ? PixelFormatInfo::SURFACE_WINDOW : 0);
	info.surfaceTypes		|= (values[WGL_DRAW_TO_BITMAP_ARB] ? PixelFormatInfo::SURFACE_PIXMAP : 0);
	info.acceleration		= translateAcceleration(values[WGL_ACCELERATION_ARB]);
	info.needPalette		= values[WGL_NEED_PALETTE_ARB] != 0;
	info.needSystemPalette	= values[WGL_NEED_SYSTEM_PALETTE_ARB] != 0;
	info.numOverlays		= values[WGL_NUMBER_OVERLAYS_ARB] != 0;
	info.numUnderlays		= values[WGL_NUMBER_UNDERLAYS_ARB] != 0;
	info.supportOpenGL		= values[WGL_SUPPORT_OPENGL_ARB] != 0;
	info.doubleBuffer		= values[WGL_DOUBLE_BUFFER_ARB] != 0;
	info.stereo				= values[WGL_STEREO_ARB] != 0;
	info.pixelType			= translatePixelType(values[WGL_PIXEL_TYPE_ARB]);
	info.redBits			= values[WGL_RED_BITS_ARB];
	info.greenBits			= values[WGL_GREEN_BITS_ARB];
	info.blueBits			= values[WGL_BLUE_BITS_ARB];
	info.alphaBits			= values[WGL_ALPHA_BITS_ARB];
	info.accumBits			= values[WGL_ACCUM_BITS_ARB];
	info.depthBits			= values[WGL_DEPTH_BITS_ARB];
	info.stencilBits		= values[WGL_STENCIL_BITS_ARB];
	info.numAuxBuffers		= values[WGL_AUX_BUFFERS_ARB];
	info.sampleBuffers		= values[WGL_SAMPLE_BUFFERS_ARB];
	info.samples			= values[WGL_SAMPLES_ARB];
	info.sRGB				= values[WGL_COLORSPACE_EXT] == WGL_COLORSPACE_SRGB_EXT;

	return info;
}

// Context

Context::Context (const Core*						core,
				  HDC								deviceCtx,
				  const Context*					sharedContext,
				  glu::ContextType					ctxType,
				  int								pixelFormat,
				  glu::ResetNotificationStrategy	resetNotificationStrategy)
	: m_core		(core)
	, m_deviceCtx	(deviceCtx)
	, m_context		(0)
{
	const Functions&		wgl				= core->getLibrary()->getFunctions();
	std::vector<int>		attribList;

	// Context version and profile
	{
		int	profileBit	= 0;
		HGLRC sharedCtx	= DE_NULL;
		int minor		= ctxType.getMinorVersion();
		int major		= ctxType.getMajorVersion();

		switch (ctxType.getProfile())
		{
			case glu::PROFILE_CORE:
				profileBit = WGL_CONTEXT_CORE_PROFILE_BIT_ARB;
				if (major == 3 && minor < 3)
					minor = 3;
				break;

			case glu::PROFILE_ES:
				profileBit = WGL_CONTEXT_ES_PROFILE_BIT_EXT;
				break;

			case glu::PROFILE_COMPATIBILITY:
				profileBit = WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB;
				break;

			default:
				TCU_THROW(NotSupportedError, "Unsupported context type for WGL");
		}

		attribList.push_back(WGL_CONTEXT_MAJOR_VERSION_ARB);
		attribList.push_back(major);
		attribList.push_back(WGL_CONTEXT_MINOR_VERSION_ARB);
		attribList.push_back(minor);
		attribList.push_back(WGL_CONTEXT_PROFILE_MASK_ARB);
		attribList.push_back(profileBit);
	}

	// Context flags
	{
		int		flags	= 0;

		if ((ctxType.getFlags() & glu::CONTEXT_FORWARD_COMPATIBLE) != 0)
		{
			if (glu::isContextTypeES(ctxType))
				TCU_THROW(InternalError, "Only OpenGL core contexts can be forward-compatible");

			flags |= WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB;
		}

		if ((ctxType.getFlags() & glu::CONTEXT_DEBUG) != 0)
			flags |= WGL_CONTEXT_DEBUG_BIT_ARB;

		if ((ctxType.getFlags() & glu::CONTEXT_ROBUST) != 0)
			flags |= WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB;

		if ((ctxType.getFlags() & glu::CONTEXT_NO_ERROR) != 0)
		{
			if (core->getLibrary()->isWglExtensionSupported("WGL_ARB_create_context_no_error"))
			{
				attribList.push_back(WGL_CONTEXT_OPENGL_NO_ERROR_ARB);
				attribList.push_back(1);
			}
			else
				TCU_THROW(NotSupportedError, "WGL_ARB_create_context_no_error is required for creating no-error contexts");
		}

		if (flags != 0)
		{
			attribList.push_back(WGL_CONTEXT_FLAGS_ARB);
			attribList.push_back(flags);
		}
	}

	if (resetNotificationStrategy != glu::RESET_NOTIFICATION_STRATEGY_NOT_SPECIFIED)
	{
		attribList.push_back(WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB);

		if (resetNotificationStrategy == glu::RESET_NOTIFICATION_STRATEGY_NO_RESET_NOTIFICATION)
			attribList.push_back(WGL_NO_RESET_NOTIFICATION_ARB);
		else if (resetNotificationStrategy == glu::RESET_NOTIFICATION_STRATEGY_LOSE_CONTEXT_ON_RESET)
			attribList.push_back(WGL_LOSE_CONTEXT_ON_RESET_ARB);
		else
			TCU_THROW(InternalError, "Unknown reset notification strategy");
	}

	// Set pixel format
	{
		PIXELFORMATDESCRIPTOR pixelFormatDesc;
		deMemset(&pixelFormatDesc, 0, sizeof(pixelFormatDesc));

		if (!DescribePixelFormat(deviceCtx, pixelFormat, sizeof(pixelFormatDesc), &pixelFormatDesc))
			throw ResourceError("DescribePixelFormat() failed");

		if (!SetPixelFormat(deviceCtx, pixelFormat, &pixelFormatDesc))
			throw ResourceError("Failed to set pixel format");
	}

	HGLRC sharedCtx = DE_NULL;
	if (DE_NULL != sharedContext)
		sharedCtx = sharedContext->m_context;

	// Terminate attribList
	attribList.push_back(0);

	// Create context
	m_context = wgl.createContextAttribsARB(deviceCtx, sharedCtx, &attribList[0]);

	if (!m_context)
		TCU_THROW(ResourceError, "Failed to create WGL context");

	if (!wgl.makeCurrent(deviceCtx, m_context))
	{
		wgl.deleteContext(m_context);
		TCU_THROW(ResourceError, "wglMakeCurrent() failed");
	}

	if (core->getLibrary()->isWglExtensionSupported("WGL_EXT_swap_control"))
		core->getLibrary()->getFunctions().swapIntervalEXT(0);
}

Context::~Context (void)
{
	const Functions& wgl = m_core->getLibrary()->getFunctions();

	wgl.makeCurrent(m_deviceCtx, NULL);
	wgl.deleteContext(m_context);
}

FunctionPtr Context::getGLFunction (const char* name) const
{
	FunctionPtr ptr = DE_NULL;

	// Try first with wglGeProcAddress()
	ptr = (FunctionPtr)m_core->getLibrary()->getFunctions().getProcAddress(name);

	// Fall-back to dynlib
	if (!ptr)
		ptr = (FunctionPtr)m_core->getLibrary()->getGLLibrary().getFunction(name);

	return ptr;
}

void Context::makeCurrent (void)
{
	const Functions& wgl = m_core->getLibrary()->getFunctions();
	if (!wgl.makeCurrent(m_deviceCtx, m_context))
		TCU_THROW(ResourceError, "wglMakeCurrent() failed");
}

void Context::swapBuffers (void) const
{
	const Functions& wgl = m_core->getLibrary()->getFunctions();
	if (!wgl.swapLayerBuffers(m_deviceCtx, WGL_SWAP_MAIN_PLANE))
		TCU_THROW(ResourceError, "wglSwapBuffers() failed");
}

bool isSupportedByTests (const PixelFormatInfo& info)
{
	if (!info.supportOpenGL)
		return false;

	if (info.acceleration != wgl::PixelFormatInfo::ACCELERATION_FULL)
		return false;

	if (info.pixelType != wgl::PixelFormatInfo::PIXELTYPE_RGBA)
		return false;

	if ((info.surfaceTypes & wgl::PixelFormatInfo::SURFACE_WINDOW) == 0)
		return false;

	if (info.needPalette || info.needSystemPalette)
		return false;

	if (info.numOverlays != 0 || info.numUnderlays != 0)
		return false;

	if (info.stereo)
		return false;

	return true;
}

int choosePixelFormat (const Core& wgl, HDC deviceCtx, const glu::RenderConfig& config)
{
	std::vector<int> pixelFormats = wgl.getPixelFormats(deviceCtx);

	for (std::vector<int>::const_iterator fmtIter = pixelFormats.begin(); fmtIter != pixelFormats.end(); ++fmtIter)
	{
		const PixelFormatInfo info = wgl.getPixelFormatInfo(deviceCtx, *fmtIter);

		// Skip formats that are fundamentally not compatible with current tests
		if (!isSupportedByTests(info))
			continue;

		if (config.redBits != glu::RenderConfig::DONT_CARE &&
			config.redBits != info.redBits)
			continue;

		if (config.greenBits != glu::RenderConfig::DONT_CARE &&
			config.greenBits != info.greenBits)
			continue;

		if (config.blueBits != glu::RenderConfig::DONT_CARE &&
			config.blueBits != info.blueBits)
			continue;

		if (config.alphaBits != glu::RenderConfig::DONT_CARE &&
			config.alphaBits != info.alphaBits)
			continue;

		if (config.depthBits != glu::RenderConfig::DONT_CARE &&
			config.depthBits != info.depthBits)
			continue;

		if (config.stencilBits != glu::RenderConfig::DONT_CARE &&
			config.stencilBits != info.stencilBits)
			continue;

		if (config.numSamples != glu::RenderConfig::DONT_CARE &&
			config.numSamples != info.samples)
			continue;

		// Passed all tests - select this.
		return info.pixelFormat;
	}

	return -1;
}

} // wgl
} // tcu
